# -*- coding: utf-8 -*-

"""
@author: 阿辉
@contact: QQ:2655399832
@Created on: 2022/9/21 16:30
@Remark:
"""
import uuid

from django_celery_beat.models import PeriodicTask, CrontabSchedule, cronexp
from rest_framework.exceptions import APIException
from rest_framework import serializers
from django_restql.fields import DynamicSerializerMethodField
from dvadmin.utils.serializers import CustomModelSerializer
from dvadmin.utils.viewset import CustomModelViewSet
from dvadmin.utils.json_response import SuccessResponse, ErrorResponse
from dvadmin_celery.views.crontab_schedule import CrontabScheduleSerializer
from dvadmin_celery import tasks
from kombu.utils.json import loads
from celery import current_app
import re
import json

CrontabSchedule.__str__ = lambda self: '{0} {1} {2} {3} {4} {5}'.format(
    cronexp(self.minute), cronexp(self.hour),
    cronexp(self.day_of_month), cronexp(self.month_of_year),
    cronexp(self.day_of_week), str(self.timezone)
)


def get_job_list():
    from application import settings
    task_list = []
    task_dict_list = []
    for app in settings.INSTALLED_APPS:
        try:
            exec(f"""from {app} import tasks""")
        except ImportError:
            pass

    current_app.loader.import_default_modules()
    task_list = list(sorted(name for name in current_app.tasks.keys() if not name.startswith('celery.')))

    for task in task_list:
        task_dict = dict()
        task_dict['label'] = task
        task_dict['value'] = task
        task_dict_list.append(task_dict)
    return {'task_list': task_list, 'task_dict_list': task_dict_list}


# 将cron表达式进行解析
def CronSlpit(cron):
    cron = cron.split(" ")
    result = {
        # "second":cron[0],
        "minute": cron[0],
        "hour": cron[1],
        "day_of_week": cron[2],
        "day_of_month": cron[3],
        "month_of_year": cron[4]
    }
    return result


class CeleryCrontabScheduleSerializer(CustomModelSerializer):
    class Meta:
        model = CrontabSchedule
        exclude = ('timezone',)


class PeriodicTasksSerializer(CustomModelSerializer):
    crontab_str = serializers.SerializerMethodField()

    class Meta:
        model = PeriodicTask
        fields = '__all__'

    def get_crontab_str(self, instance):
        cron = instance.crontab
        serializers = CrontabScheduleSerializer(cron)
        # return [{"id": instance.crontab.id,"value":instance.crontab.__str__()}]
        # return serializers.data
        return instance.crontab.__str__()


def is_json(str):
    str = str.replace("\t","")
    str = str.replace("\n","")
    pattern = r'^\s*({.*}|\[.*\])\s*$'
    if re.match(pattern, str) is None:
        return False
    else:
        try:
            json.loads(str)
            return True
        except ValueError:
            return False

class CeleryTaskModelViewSet(CustomModelViewSet):
    """
    CeleryTask 添加任务调度
    """

    queryset = PeriodicTask.objects.exclude(name="celery.backend_cleanup")
    serializer_class = PeriodicTasksSerializer
    filter_fields = ['name', 'task', 'enabled', 'crontab']

    # permission_classes = []
    # authentication_classes = []

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True, request=request)
            return self.get_paginated_response(serializer.data)
        serializer = self.get_serializer(queryset, many=True, request=request)
        return SuccessResponse(data=serializer.data, msg="获取成功")

    def job_list(self, request, *args, **kwargs):
        """获取所有任务"""
        result = get_job_list()
        task_list = result.get('task_dict_list')
        return SuccessResponse(msg='获取成功', data=task_list, total=len(task_list))

    def create(self, request, *args, **kwargs):
        body_data = request.data.copy()
        cron = body_data.get('crontab_str')
        cron_data = CronSlpit(cron)

        task = body_data.get('task')
        result = None
        task_list = get_job_list()
        task_list = task_list.get('task_list')
        if task in task_list:

            # 添加crontab
            serializer = CeleryCrontabScheduleSerializer(data=cron_data, request=request)
            serializer.is_valid(raise_exception=True)
            print(cron_data)
            schedule, created = CrontabSchedule.objects.get_or_create(**cron_data)  # 手动创建crontab，避免重复创建

            # 添加任务
            body_data['crontab'] = schedule.id
            body_data['enabled'] = False
            if body_data.get('kwargs') is not None:
                kwargs = str(body_data['kwargs'])
                if is_json(kwargs)==False:
                    return ErrorResponse(msg="参数格式不是json格式")
                body_data['kwargs']=kwargs
            serializer = self.get_serializer(data=body_data, request=request,
                                             context={"crontab": serializer.data.get('id')})

            res = serializer.is_valid(raise_exception=True)
            serializer.errors
            if not res:
                raise APIException({"msg": f"添加失败，已经有一个名为 {body_data['name']} 的任务了"}, code=4000)
            self.perform_create(serializer)
            result = serializer.data
            return SuccessResponse(msg="添加成功", data=result)
        else:
            return ErrorResponse(msg="添加失败,没有该任务", data=None)

    def update(self, request, *args, **kwargs):
        instance = self.get_object()
        body_data = request.data
        instance.name = body_data.get('name')  # 更新名称
        instance.task = body_data.get('task')  # 更新任务
        instance.args = body_data.get('args')  # 更新参数
        if body_data.get('kwargs') is not None:
            kwargs_str = str(body_data['kwargs'])
            if is_json(kwargs_str) == False:
              return ErrorResponse(msg="参数格式不是json格式")
        instance.kwargs = body_data.get('kwargs')  # 更新参数

        # 更新crontab
        cron = body_data.get('crontab_str')
        cron_data = CronSlpit(cron)
        schedule, created = CrontabSchedule.objects.get_or_create(**cron_data)  # 手动创建crontab，避免重复创建
        instance.crontab = schedule

        instance.description = body_data.get('description')  # 更新描述
        instance.save()
        return SuccessResponse(msg="修改成功", data=None)

    def destroy(self, request, *args, **kwargs):
        """删除定时任务"""
        instance = self.get_object()
        self.perform_destroy(instance)
        return SuccessResponse(data=[], msg="删除成功")

    def update_status(self, request, *args, **kwargs):
        """开始/暂停任务"""
        instance = self.get_object()
        body_data = request.data
        instance.enabled = body_data.get('enabled')
        instance.save()
        return SuccessResponse(msg="修改成功", data=None)

    def run_task(self, request, *args, **kwargs):
        # 加载环境
        from application import settings
        for app in settings.INSTALLED_APPS:
            try:
                exec(f"""from {app} import tasks""")
            except ImportError:
                pass
        print("load success")
        current_app.loader.import_default_modules()
        instance = self.get_object()
        task = current_app.tasks.get(instance.task)
        task_id = task.apply_async(args=loads(instance.args), kwargs=loads(instance.kwargs), queue=instance.queue,
                                   periodic_task_name=instance.name)
        return SuccessResponse(msg="Task {}({}) 运行成功".format(instance.name, instance.task), data=None)
